package org.hawk.gwt.ppc.parser.impl;

import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.expect;
import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.lookupNextNonComment;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.hawk.gwt.ppc.parser.BlockDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.parser.DescriptorIndexOutOfBoundException;
import org.hawk.gwt.ppc.parser.DescriptorVisitor;
import org.hawk.gwt.ppc.parser.DoWhileExpressionDescriptor;
import org.hawk.gwt.ppc.parser.ExpressionDescriptor;
import org.hawk.gwt.ppc.parser.ForExpressionDescriptor;
import org.hawk.gwt.ppc.parser.ForeachExpressionDescriptor;
import org.hawk.gwt.ppc.parser.IfExpressionDescriptor;
import org.hawk.gwt.ppc.parser.JavaKeywords;
import org.hawk.gwt.ppc.parser.PreprocessorParseException;
import org.hawk.gwt.ppc.parser.SwitchExpressionDescriptor;
import org.hawk.gwt.ppc.parser.SynchronizedBlockDescriptor;
import org.hawk.gwt.ppc.parser.Token;
import org.hawk.gwt.ppc.parser.TryCatchFinallyExpressionDescriptor;
import org.hawk.gwt.ppc.parser.WhileExpressionDescriptor;
import org.hawk.gwt.ppc.utils.PreprocessorException;

final class BlockExpressionDescriptorParser implements DescriptorParser {

	public boolean accepts(Token token) {
		return token.equalsTo(JavaKeywords.IF) || 
				token.equalsTo(JavaKeywords.SWITCH) ||
				token.equalsTo(JavaKeywords.FOR) ||
				token.equalsTo(JavaKeywords.WHILE) ||
				token.equalsTo(JavaKeywords.DO) ||
				token.equalsTo(JavaKeywords.TRY) ||
				token.equalsTo(JavaKeywords.SYNCHRONIZED);
	}

	@SuppressWarnings("unchecked")
	public Descriptor parse(TokenBasedJavaParser parser, TokenSource tokenizer,
			Object... params) throws PreprocessorException {
		Token token = tokenizer.current();
		List<String> labels = (List<String>)(params.length > 0 ? params[0] : Collections.emptyList());
		if (token.equalsTo(JavaKeywords.IF)) {
			return parseIf(parser, tokenizer, labels);
		}
		if (token.equalsTo(JavaKeywords.WHILE)) {
			return parseWhile(parser, tokenizer, labels);
		}
		if (token.equalsTo(JavaKeywords.FOR)) {
			return parseFor(parser, tokenizer, labels);
		}
		if (token.equalsTo(JavaKeywords.TRY)) {
			return parseTryCatchFinally(parser, tokenizer, labels);
		}
		if (token.equalsTo(JavaKeywords.DO)) {
			return parseDoWhile(parser, tokenizer, labels);
		}
		if (token.equalsTo(JavaKeywords.SWITCH)) {
			return parseSwitch(parser, tokenizer, labels);
		}
		if (token.equalsTo(JavaKeywords.SYNCHRONIZED)) {
			return parseSynchronizedBlock(parser, tokenizer, labels);
		}
		throw new PreprocessorParseException(token);
	}

	private Descriptor parseSynchronizedBlock(TokenBasedJavaParser parser,
			TokenSource tokenizer, List<String> labels) throws PreprocessorException {
		Token first = tokenizer.current();
		ExpressionDescriptor synchronizedDescriptor = (ExpressionDescriptor) parseCondition(parser, tokenizer);
		lookupNextNonComment(tokenizer, false);
		if (!tokenizer.current().equalsTo('{')) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		BlockDescriptor block = (BlockDescriptor)expect(parser, tokenizer, null, DescriptorType.BLOCK);
		Token last = ((AbstractDescriptor)block).getLastToken();
		return new SynchronizedBlock(first, last, synchronizedDescriptor, block, labels);
	}

	private Descriptor parseTryCatchFinally(TokenBasedJavaParser parser,
			TokenSource tokenizer, List<String> labels) throws PreprocessorException {
		Token first = tokenizer.current();
		lookupNextNonComment(tokenizer, false);
		if (!tokenizer.current().equalsTo('{')) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		BlockDescriptor tryExpression = (BlockDescriptor) expect(parser, tokenizer, null, DescriptorType.BLOCK);
		List<ExpressionDescriptor> catchExpressions = new ArrayList<ExpressionDescriptor>(1);
		Map<ExpressionDescriptor, BlockDescriptor> catchBlocks = new HashMap<ExpressionDescriptor, BlockDescriptor>();
		while (tokenizer.current().equalsTo(JavaKeywords.CATCH)) {
			ExpressionDescriptor catchExpression = (ExpressionDescriptor) parseCondition(parser, tokenizer);
			lookupNextNonComment(tokenizer, false);
			if (!tokenizer.current().equalsTo('{')) {
				throw new PreprocessorParseException(tokenizer.current());
			}
			BlockDescriptor catchBlock = (BlockDescriptor) expect(parser, tokenizer, null, DescriptorType.BLOCK);
			catchExpressions.add(catchExpression);
			catchBlocks.put(catchExpression, catchBlock);
		}
		BlockDescriptor finallyExpression = null;
		if (tokenizer.current().equalsTo(JavaKeywords.FINALLY)) {
			lookupNextNonComment(tokenizer, false);
			if (!tokenizer.current().equalsTo('{')) {
				throw new PreprocessorParseException(tokenizer.current());
			}
			finallyExpression = (BlockDescriptor) expect(parser, tokenizer, null, DescriptorType.BLOCK);
		}
		if (catchExpressions.isEmpty() && finallyExpression == null) {
			throw new PreprocessorParseException("Expected either catch or finally", tokenizer.current());
		}
		Token last;
		if (finallyExpression != null) {
			last = ((AbstractDescriptor)finallyExpression).getLastToken();
		} else {
			last = ((AbstractDescriptor)catchBlocks.get(catchExpressions.get(catchExpressions.size() - 1))).getLastToken();
		}
		
		return new TryCatchFinallyExpression(first, last, tryExpression, catchExpressions, catchBlocks, finallyExpression, labels);
	}

	private Descriptor parseDoWhile(TokenBasedJavaParser parser,
			TokenSource tokenizer, List<String> labels) throws PreprocessorException {
		Token first = tokenizer.current();
		lookupNextNonComment(tokenizer, false);
		Descriptor bodyExpression = expect(parser, tokenizer, null, DescriptorType.BLOCK, DescriptorType.LINE_EXPRESSION, 
				DescriptorType.BLOCK_EXPRESSION, DescriptorType.KEYWORD_EXPRESSION);
		if (!tokenizer.current().equalsTo(JavaKeywords.WHILE)) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		Descriptor whileCondition = parseCondition(parser, tokenizer);
		lookupNextNonComment(tokenizer, false);
		if (!tokenizer.current().equalsTo(';')) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		Token last = tokenizer.current();
		lookupNextNonComment(tokenizer, false);
		return new DoWhileExpression(first, last, bodyExpression, whileCondition, labels);
	}

	private Descriptor parseFor(TokenBasedJavaParser parser,
			TokenSource tokenizer, List<String> labels) throws PreprocessorException {
		Token first = tokenizer.current();
		lookupNextNonComment(tokenizer, false);
		if (!tokenizer.current().equalsTo('(')) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		LinkedList<Token> condition = new LinkedList<Token>();
		int deep = 1;
		boolean foreach = true;
		while (deep > 0) {
			lookupNextNonComment(tokenizer, false);
			if (tokenizer.current().equalsTo(')')) {
				deep--;
			} else if (tokenizer.current().equalsTo('(')) {
				deep++;
			} else if (tokenizer.current().equalsTo(';')) {
				foreach = false;
			}
			condition.add(tokenizer.current());
		}
		condition.removeLast();
		IteratorTokenSource iterator = new IteratorTokenSource(condition.iterator());
		iterator.next();
		
		lookupNextNonComment(tokenizer, false);
		Descriptor bodyExpression = expect(parser, tokenizer, null, DescriptorType.BLOCK, DescriptorType.LINE_EXPRESSION, 
				DescriptorType.BLOCK_EXPRESSION, DescriptorType.KEYWORD_EXPRESSION);
		
		Token last = ((AbstractDescriptor)bodyExpression).getLastToken();
		if (foreach) {
			return parseForeach(first, last, parser, iterator, bodyExpression, labels);
		}
		Descriptor initializer = DescriptorType.LINE_EXPRESSION.parse(parser, iterator, true);
		Descriptor conditionExpression = DescriptorType.LINE_EXPRESSION.parse(parser, iterator, true);
		Descriptor postExpression = DescriptorType.LINE_EXPRESSION.parse(parser, iterator, true);
		
		return new ForExpression(first, last, initializer, conditionExpression, postExpression, bodyExpression, labels);
	}

	private Descriptor parseForeach(Token first, Token last, TokenBasedJavaParser parser,
			IteratorTokenSource iterator, Descriptor bodyExpression, List<String> labels) throws PreprocessorException {
		List<Token> variable = new LinkedList<Token>();
		while (!iterator.current().equalsTo(':')) {
			variable.add(iterator.current());
			iterator.next();
		}
		iterator.next();
		
		IteratorTokenSource variableIterator = new IteratorTokenSource(variable.iterator());
		variableIterator.next();
		
		Descriptor variableDescriptor = DescriptorType.LINE_EXPRESSION.parse(parser, variableIterator, true);
		Descriptor collectionDescriptor = DescriptorType.LINE_EXPRESSION.parse(parser, iterator, true);
		
		return new ForeachExpression(first, last, variableDescriptor, collectionDescriptor,  bodyExpression, labels);
	}

	private Descriptor parseWhile(TokenBasedJavaParser parser,
			TokenSource tokenizer, List<String> labels) throws PreprocessorParseException, PreprocessorException {
		Token first = tokenizer.current();
		Descriptor whileCondition = parseCondition(parser, tokenizer);
		lookupNextNonComment(tokenizer, false);
		Descriptor bodyExpression = expect(parser, tokenizer, null, DescriptorType.BLOCK, DescriptorType.LINE_EXPRESSION, 
				DescriptorType.BLOCK_EXPRESSION, DescriptorType.KEYWORD_EXPRESSION);
		Token last = ((AbstractDescriptor)bodyExpression).getLastToken();
		return new WhileExpression(first, last, (ExpressionDescriptor)whileCondition, (ExpressionDescriptor)bodyExpression, labels);
	}

	private Descriptor parseSwitch(TokenBasedJavaParser parser, TokenSource tokenizer, List<String> labels) throws PreprocessorParseException, PreprocessorException {
		Token first = tokenizer.current();
		Descriptor switchDescriptor = parseCondition(parser, tokenizer);
		lookupNextNonComment(tokenizer, false);
		if (!tokenizer.current().equalsTo('{')) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		lookupNextNonComment(tokenizer, false);
		
		List<ExpressionDescriptor> cases = new ArrayList<ExpressionDescriptor>();
		Map<ExpressionDescriptor, List<ExpressionDescriptor>> caseExpressions = new HashMap<ExpressionDescriptor, List<ExpressionDescriptor>>();
		List<ExpressionDescriptor> defaultExpression = null;
		while (!tokenizer.current().equalsTo('}')) {
			if (tokenizer.current().equalsTo(JavaKeywords.CASE)) {
				ExpressionDescriptor caseLabel = readCase(parser, tokenizer);
				cases.add(caseLabel);
				List<ExpressionDescriptor> bodyExpression = readCaseOrDefaultBody(parser, tokenizer);
				caseExpressions.put(caseLabel, bodyExpression);
			} else if (tokenizer.current().equalsTo(JavaKeywords.DEFAULT)) {
				if (defaultExpression != null) {
					throw new PreprocessorParseException("Dublicated default statement", tokenizer.current());
				}
				lookupNextNonComment(tokenizer, false);
				if (!tokenizer.current().equalsTo(':')) {
					throw new PreprocessorParseException(tokenizer.current());
				}
				lookupNextNonComment(tokenizer, false);
				defaultExpression = readCaseOrDefaultBody(parser, tokenizer);
			} else {
				throw new PreprocessorParseException("Expected case: or default:", tokenizer.current());
			}
		}
		Token last = tokenizer.current();
		lookupNextNonComment(tokenizer, false);
		return new SwitchExpression(first, last, switchDescriptor, cases, caseExpressions, defaultExpression, labels);
	}

	private ExpressionDescriptor readCase(TokenBasedJavaParser parser,
			TokenSource tokenizer) throws PreprocessorException {
		int deep = 0;
		lookupNextNonComment(tokenizer, false);
		List<Token> caseExpr = new LinkedList<Token>();
		while (deep > 0 || !tokenizer.current().equalsTo(':')) {
			caseExpr.add(tokenizer.current());
			lookupNextNonComment(tokenizer, false);
			if (tokenizer.current().equalsTo('(')) {
				deep++;
			} else if (tokenizer.current().equalsTo(')')) {
				deep--;
			}
		}
		lookupNextNonComment(tokenizer, false);
		TokenSource iterator = new IteratorTokenSource(caseExpr.iterator());
		iterator.next();
		return (ExpressionDescriptor) DescriptorType.LINE_EXPRESSION.parse(parser, iterator , true);
	}

	private List<ExpressionDescriptor> readCaseOrDefaultBody(TokenBasedJavaParser parser, TokenSource tokenizer) throws PreprocessorException {
		List<ExpressionDescriptor> result = new ArrayList<ExpressionDescriptor>();
		while (!tokenizer.current().equalsTo(JavaKeywords.CASE) &&
				!tokenizer.current().equalsTo(JavaKeywords.DEFAULT) &&
				!tokenizer.current().equalsTo('}')) {
			result.add((ExpressionDescriptor) expect(parser, tokenizer, null, DescriptorType.BLOCK, DescriptorType.LINE_EXPRESSION, 
					DescriptorType.BLOCK_EXPRESSION, DescriptorType.KEYWORD_EXPRESSION));
		}
		return Collections.unmodifiableList(result);
	}

	private Descriptor parseIf(TokenBasedJavaParser parser,
			TokenSource tokenizer, List<String> labels) throws PreprocessorException {
		Token first = tokenizer.current();
		Descriptor conditionDescriptor = parseCondition(parser, tokenizer);
		lookupNextNonComment(tokenizer, false);
		Descriptor bodyExpression = expect(parser, tokenizer, null, DescriptorType.BLOCK, DescriptorType.LINE_EXPRESSION, 
				DescriptorType.BLOCK_EXPRESSION, DescriptorType.KEYWORD_EXPRESSION);
		if (!tokenizer.current().equalsTo(JavaKeywords.ELSE)) {
			Token last =((AbstractDescriptor)bodyExpression).getLastToken();
			return new IfExpression(first, last, conditionDescriptor, bodyExpression, null, labels);
		}
		lookupNextNonComment(tokenizer, false);
		Descriptor elseExpression = expect(parser, tokenizer, null, DescriptorType.BLOCK, DescriptorType.LINE_EXPRESSION, 
				DescriptorType.BLOCK_EXPRESSION, DescriptorType.KEYWORD_EXPRESSION);
		Token last =((AbstractDescriptor)elseExpression).getLastToken();
		return new IfExpression(first, last, conditionDescriptor, bodyExpression, elseExpression, labels);
	}

	private Descriptor parseCondition(TokenBasedJavaParser parser,
			TokenSource tokenizer) throws PreprocessorException,
			PreprocessorParseException {
		lookupNextNonComment(tokenizer, false);
		if (!tokenizer.current().equalsTo('(')) {
			throw new PreprocessorParseException(tokenizer.current());
		}
		LinkedList<Token> condition = new LinkedList<Token>();
		int deep = 1;
		while (deep > 0) {
			lookupNextNonComment(tokenizer, false);
			if (tokenizer.current().equalsTo(')')) {
				deep--;
			} else if (tokenizer.current().equalsTo('(')) {
				deep++;
			}
			condition.add(tokenizer.current());
		}
		condition.removeLast();
		IteratorTokenSource iterator = new IteratorTokenSource(condition.iterator());
		iterator.next();
		return DescriptorType.LINE_EXPRESSION.parse(parser, iterator, true);
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private static final class IfExpression extends AbstractExpressionDescriptor implements IfExpressionDescriptor {
		
		private ExpressionDescriptor condition;
		private ExpressionDescriptor expression;
		private ExpressionDescriptor elseExpression;
		private List<String> labels;

		IfExpression(Token first, Token last, Descriptor conditionDescriptor,
				Descriptor bodyExpression, Descriptor elseExpression,
				List<String> labels) {
			super(first, last);
			
			this.condition = (ExpressionDescriptor)conditionDescriptor;
			this.expression = (ExpressionDescriptor)bodyExpression;
			this.elseExpression = (ExpressionDescriptor)elseExpression;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getCondition()
		 */
		public ExpressionDescriptor getCondition() {
			return condition;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getExpression()
		 */
		public ExpressionDescriptor getExpression() {
			return expression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.IfExpressionDescriptor#getElseExpression()
		 */
		public ExpressionDescriptor getElseExpression() {
			return elseExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			condition.visit(visitor);
			expression.visit(visitor);
			if (elseExpression != null) elseExpression.visit(visitor);
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class SwitchExpression extends AbstractExpressionDescriptor implements SwitchExpressionDescriptor {
		
		private List<String> labels;
		private ExpressionDescriptor switchExpression;
		private List<ExpressionDescriptor> cases;
		private Map<ExpressionDescriptor, List<ExpressionDescriptor>> caseExpressions;
		private List<ExpressionDescriptor> defaultExpression;

		SwitchExpression(Token first, Token last,
				Descriptor switchExpression,
				List<ExpressionDescriptor> cases,
				Map<ExpressionDescriptor, List<ExpressionDescriptor>> caseExpressions,
				List<ExpressionDescriptor> defaultExpression, List<String> labels) {
			super(first, last);
			
			this.switchExpression = (ExpressionDescriptor) switchExpression;
			this.cases = Collections.unmodifiableList(cases);
			this.caseExpressions = caseExpressions;
			this.defaultExpression = defaultExpression;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.SwitchExpressionDescriptor#getSwitchExpression()
		 */
		public ExpressionDescriptor getSwitchExpression() {
			return switchExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.SwitchExpressionDescriptor#getCases()
		 */
		public List<ExpressionDescriptor> getCases() {
			return cases;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.SwitchExpressionDescriptor#getCaseExpression(org.hawk.gwt.ppc.parser.ExpressionDescriptor)
		 */
		public List<ExpressionDescriptor> getCaseExpression(
				ExpressionDescriptor caseExpression) {
			return caseExpressions.get(caseExpression);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.SwitchExpressionDescriptor#getDefault()
		 */
		public List<ExpressionDescriptor> getDefault() {
			return defaultExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			
			switchExpression.visit(visitor);
			
			for (ExpressionDescriptor cs : cases) {
				cs.visit(visitor);
				for (ExpressionDescriptor expr : caseExpressions.get(cs)) {
					expr.visit(visitor);
				}
			}
			
			if (defaultExpression != null) {
				for (ExpressionDescriptor expr : defaultExpression) {
					expr.visit(visitor);
				}
			}
		}
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final class WhileExpression extends AbstractExpressionDescriptor implements WhileExpressionDescriptor {
		
		private ExpressionDescriptor whileCondition;
		private ExpressionDescriptor bodyExpression;
		private List<String> labels;

		WhileExpression(Token first, Token last, ExpressionDescriptor whileCondition,
				ExpressionDescriptor bodyExpression, List<String> labels) {
			super(first, last);
			
			this.whileCondition = whileCondition;
			this.bodyExpression = bodyExpression;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getCondition()
		 */
		public ExpressionDescriptor getCondition() {
			return whileCondition;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getExpression()
		 */
		public ExpressionDescriptor getExpression() {
			return bodyExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			whileCondition.visit(visitor);
			bodyExpression.visit(visitor);
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class ForExpression extends AbstractExpressionDescriptor implements ForExpressionDescriptor {

		private ExpressionDescriptor initializer;
		private ExpressionDescriptor conditionExpression; 
		private ExpressionDescriptor postExpression;
		private ExpressionDescriptor bodyExpression; 
		private List<String> labels;
		
		ForExpression(Token first, Token last, Descriptor initializer,
				Descriptor conditionExpression, Descriptor postExpression,
				Descriptor bodyExpression, List<String> labels) {
			super(first, last);
			
			this.initializer = (ExpressionDescriptor) initializer;
			this.conditionExpression = (ExpressionDescriptor) conditionExpression;
			this.postExpression = (ExpressionDescriptor) postExpression;
			this.bodyExpression = (ExpressionDescriptor) bodyExpression;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getCondition()
		 */
		public ExpressionDescriptor getCondition() {
			return conditionExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getExpression()
		 */
		public ExpressionDescriptor getExpression() {
			return bodyExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ForExpressionDescriptor#getInitializer()
		 */
		public ExpressionDescriptor getInitializer() {
			return initializer;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ForExpressionDescriptor#getPostExpression()
		 */
		public ExpressionDescriptor getPostExpression() {
			return postExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			initializer.visit(visitor);
			conditionExpression.visit(visitor);
			postExpression.visit(visitor);
			bodyExpression.visit(visitor);
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class ForeachExpression extends AbstractExpressionDescriptor implements ForeachExpressionDescriptor {

		private ExpressionDescriptor variableDescriptor;
		private ExpressionDescriptor collectionDescriptor; 
		private ExpressionDescriptor bodyExpression;
		private List<String> labels;
		
		ForeachExpression(Token first, Token last, Descriptor variableDescriptor,
				Descriptor collectionDescriptor, Descriptor bodyExpression,
				List<String> labels) {
			super(first, last);
			
			this.variableDescriptor = (ExpressionDescriptor)variableDescriptor;
			this.collectionDescriptor = (ExpressionDescriptor)collectionDescriptor;
			this.bodyExpression = (ExpressionDescriptor)bodyExpression;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ForeachExpressionDescriptor#getVariable()
		 */
		public ExpressionDescriptor getVariable() {
			return variableDescriptor;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ForeachExpressionDescriptor#getCollection()
		 */
		public ExpressionDescriptor getCollection() {
			return collectionDescriptor;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ForeachExpressionDescriptor#getExpression()
		 */
		public ExpressionDescriptor getExpression() {
			return bodyExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			variableDescriptor.visit(visitor);
			collectionDescriptor.visit(visitor);
			bodyExpression.visit(visitor);
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class DoWhileExpression extends AbstractExpressionDescriptor implements DoWhileExpressionDescriptor {

		private ExpressionDescriptor bodyExpression;
		private ExpressionDescriptor whileCondition;
		private List<String> labels;
		
		DoWhileExpression(Token first, Token last, Descriptor bodyExpression,
				Descriptor whileCondition, List<String> labels) {
			super(first, last);
			
			this.bodyExpression = (ExpressionDescriptor)bodyExpression;
			this.whileCondition = (ExpressionDescriptor)whileCondition;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getCondition()
		 */
		public ExpressionDescriptor getCondition() {
			return whileCondition;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ConditionalExpressionDescriptor#getExpression()
		 */
		public ExpressionDescriptor getExpression() {
			return bodyExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			bodyExpression.visit(visitor);
			whileCondition.visit(visitor);
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class TryCatchFinallyExpression extends AbstractExpressionDescriptor 
			implements TryCatchFinallyExpressionDescriptor {

		private BlockDescriptor tryExpression;
		private List<ExpressionDescriptor> catchExpressions;
		private Map<ExpressionDescriptor, BlockDescriptor> catchBlocks;
		private BlockDescriptor finallyExpression;
		private List<String> labels;
		
		TryCatchFinallyExpression(Token first, Token last, BlockDescriptor tryExpression,
				List<ExpressionDescriptor> catchExpressions,
				Map<ExpressionDescriptor, BlockDescriptor> catchBlocks,
				BlockDescriptor finallyExpression, List<String> labels) {
			super(first, last);
			
			this.tryExpression = tryExpression;
			this.catchExpressions = Collections.unmodifiableList(catchExpressions);
			this.catchBlocks = catchBlocks;
			this.finallyExpression = finallyExpression;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TryCatchFinallyExpressionDescriptor#getTryBlock()
		 */
		public BlockDescriptor getTryBlock() {
			return tryExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TryCatchFinallyExpressionDescriptor#getCatchClasuses()
		 */
		public List<ExpressionDescriptor> getCatchClasuses() {
			return catchExpressions;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TryCatchFinallyExpressionDescriptor#getCatchBlock(org.hawk.gwt.ppc.parser.ExpressionDescriptor)
		 */
		public BlockDescriptor getCatchBlock(ExpressionDescriptor catchClause) {
			return catchBlocks.get(catchClause);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TryCatchFinallyExpressionDescriptor#getFinallyBlock()
		 */
		public BlockDescriptor getFinallyBlock() {
			return finallyExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			tryExpression.visit(visitor);
			
			for (ExpressionDescriptor catchExpr : catchExpressions) {
				catchExpr.visit(visitor);
				catchBlocks.get(catchExpr).visit(visitor);
			}
			if (finallyExpression != null) {
				finallyExpression.visit(visitor);
			}
		}
	}
	
	private final static class SynchronizedBlock extends AbstractExpressionDescriptor implements SynchronizedBlockDescriptor {
		
		private ExpressionDescriptor synchronizedDescriptor;
		private BlockDescriptor block;
		private List<String> labels;

		SynchronizedBlock(Token first, Token last, ExpressionDescriptor synchronizedDescriptor,
				BlockDescriptor block, List<String> labels) {
			super(first, last);
			
			this.synchronizedDescriptor = synchronizedDescriptor;
			this.block = block;
			this.labels = labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ModificatedDescriptor#getModifiers()
		 */
		public List<String> getModifiers() {
			return Collections.singletonList(JavaKeywords.SYNCHRONIZED);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.DescriptorContainer#get(int)
		 */
		public Descriptor get(int index)
				throws DescriptorIndexOutOfBoundException {
			return block.get(index);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.DescriptorContainer#remove(int)
		 */
		public Descriptor remove(int index)
				throws DescriptorIndexOutOfBoundException {
			return block.remove(index);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.DescriptorContainer#size()
		 */
		public int size() {
			return block.size();
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Iterable#iterator()
		 */
		public Iterator<Descriptor> iterator() {
			return block.iterator();
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.LabeledBlockDescriptor#getLabels()
		 */
		public List<String> getLabels() {
			return labels;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.SynchronizedBlockDescriptor#getTarget()
		 */
		public ExpressionDescriptor getTarget() {
			return synchronizedDescriptor;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			synchronizedDescriptor.visit(visitor);
			Iterator<Descriptor> iterator = iterator();
			while (iterator.hasNext()) {
				iterator.next().visit(visitor);
			}
		}
	}
}
